/*

 * Copyright (c) 2017-2018, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package of.hotswapinject.core.plugin;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PluginClassLoader extends URLClassLoader {
    protected final SecurityManager securityManager;
    private Map<String, Class> resourceEntries = new HashMap<String, Class>();
    private ClassLoader javaseClassLoader;
    protected final ClassLoader parent;
    private boolean hasExternalRepositories = false;

    public PluginClassLoader(ClassLoader parent, List<String> fileNames) {
        super(new URL[0], parent);
        for(String fileName : fileNames) {
            String path = "file:" + fileName;
            try {
                this.addURL(new URL(path));
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        this.parent = parent;
        securityManager = System.getSecurityManager();
        if(securityManager != null) {
            refreshPolicy();
        }

        ClassLoader classLoader = String.class.getClassLoader();
        if (classLoader == null) {
            for(classLoader = getSystemClassLoader(); classLoader.getParent() != null; classLoader = classLoader.getParent()) {
                ;
            }
        }
        this.javaseClassLoader = classLoader;
    }

    protected void refreshPolicy() {
        try {
            Policy policy = Policy.getPolicy();
            policy.refresh();
        } catch (AccessControlException var2) {
            ;
        }

    }
    private String binaryNameToPath(String binaryName, boolean withLeadingSlash) {
        StringBuilder path = new StringBuilder(7 + binaryName.length());
        if (withLeadingSlash) {
            path.append('/');
        }

        path.append(binaryName.replace('.', '/'));
        path.append(".class");
        return path.toString();
    }

    protected Class<?> findLoadedClass0(String name) {
        String path = this.binaryNameToPath(name, true);
        Class entry = (Class)this.resourceEntries.get(path);
        return entry != null ? entry : null;
    }


    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return this.loadClass(name, false);
    }

    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (this.getClassLoadingLock(name)) {
            Class<?> clazz = null;
            clazz = this.findLoadedClass0(name);
            if (clazz != null) {
                if (resolve) {
                    this.resolveClass(clazz);
                }

                return clazz;
            } else {
                clazz = this.findLoadedClass(name);
                if (clazz != null) {
                    if (resolve) {
                        this.resolveClass(clazz);
                    }

                    return clazz;
                } else {
                    String resourceName = this.binaryNameToPath(name, false);
                    ClassLoader javaseLoader = this.javaseClassLoader;

                    boolean tryLoadingFromJavaseLoader;
                    try {
                        tryLoadingFromJavaseLoader = javaseLoader.getResource(resourceName) != null;
                    } catch (Throwable var13) {
                        tryLoadingFromJavaseLoader = true;
                    }

                    Class var10000;
                    if (tryLoadingFromJavaseLoader) {
                        label210:
                        {
                            try {
                                clazz = javaseLoader.loadClass(name);
                                if (clazz == null) {
                                    break label210;
                                }

                                if (resolve) {
                                    this.resolveClass(clazz);
                                }

                                var10000 = clazz;
                            } catch (ClassNotFoundException var15) {
                                break label210;
                            }

                            return var10000;
                        }
                    }

                    if (this.securityManager != null) {
                        int i = name.lastIndexOf(46);
                        if (i >= 0) {
                            try {
                                this.securityManager.checkPackageAccess(name.substring(0, i));
                            } catch (SecurityException var12) {
                                String error = "Security Violation, attempt to use Restricted Class: " + name;
                                throw new ClassNotFoundException(error, var12);
                            }
                        }
                    }

                    try {
                        clazz = this.findClass(name);
                        if (clazz != null) {
                            if (resolve) {
                                this.resolveClass(clazz);
                            }

                            var10000 = clazz;
                            return var10000;
                        }
                    } catch (ClassNotFoundException var17) {
                        ;
                    }

                    try {
                        clazz = Class.forName(name, false, this.parent);
                        if (clazz == null) {
                            throw new ClassNotFoundException(name);
                        }

                        if (resolve) {
                            this.resolveClass(clazz);
                        }

                        var10000 = clazz;
                    } catch (ClassNotFoundException var14) {
                        throw new ClassNotFoundException(name);
                    }
                    return var10000;
                }
            }
        }
    }
}

